package org.tamal.jsf.bean;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

import org.json.JSONObject;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.SortDirection;

/**
 * This class is responsible for generating table data.
 * @author Tamal Kanti Nath
 */
@ManagedBean(name = "entity")
@ViewScoped
public final class EntityBean implements ActionListener, Serializable {

    private static final long serialVersionUID = 3319158207862639303L;
    private static final Logger log = Logger.getLogger(EntityBean.class.getName());
    private static final DatastoreService datastore =
            DatastoreServiceFactory.getDatastoreService();
    private static final int size = 10;
    private final String kind;

    private List<Entity> entities;
    private int count;
    private int page;
    private String order;
    private boolean desc;

    /**
     * Initializes EntityBean.
     */
    public EntityBean() {
        FacesContext fc = FacesContext.getCurrentInstance();
        String view = fc.getViewRoot().getViewId();
        JSONObject json = new JSONObject(System.getProperty("EntityBean"));
        kind = json.getString(view);
        loadEntities();
    }

    /**
     * Returns the entities.
     * @return the entities
     */
    public List<Entity> getEntities() {
        return entities;
    }

    /**
     * Returns the count.
     * @return the count
     */
    public int getCount() {
        return count;
    }

    /**
     * Returns the page.
     * @return the page
     */
    public int getPage() {
        return page;
    }

    /**
     * Returns the index of first record.
     * @return the index of first record
     */
    public int getFirst() {
        return count == 0 ? 0 : page * size + 1;
    }

    /**
     * Returns the index of last record.
     * @return the index of last record
     */
    public int getLast() {
        return Math.min((page + 1) * size, count);
    }

    /**
     * Returns the size.
     * @return the size
     */
    @SuppressWarnings("static-method")
    public int getSize() {
        return size;
    }

    /**
     * Returns the order.
     * @return the order
     */
    public String getOrder() {
        return order;
    }

    /**
     * Returns the descending.
     * @return the descending
     */
    public boolean isDesc() {
        return desc;
    }

    private void loadEntities() {
        log.info("page: " + page + " order: " + order + " desc: " + desc);
        Query query = new Query(kind);
        if (order != null) {
            SortDirection direction = desc ? SortDirection.DESCENDING
                    : SortDirection.ASCENDING;
            query.addSort(order, direction);
        }
        PreparedQuery preparedQuery = datastore.prepare(query);
        FetchOptions fetchOptions = FetchOptions.Builder
                .withOffset(page * size).limit(size);
        entities = preparedQuery.asList(fetchOptions);
        count = preparedQuery.countEntities(FetchOptions.Builder.withDefaults());
    }

    /**
     * Listen to an action event.
     * @param event the ActionEvent
     */
    @Override
    public void processAction(ActionEvent event) {
        String id = event.getComponent().getId();
        log.info("Event: " + id);
        Map<String, Object> attributes = event.getComponent().getAttributes();
        switch (id) {
        case "delete":
            String name = (String) attributes.get("name");
            Key key = KeyFactory.createKey(kind, name);
            datastore.delete(key);
            break;
        case "first":
            page = 0;
            break;
        case "previous":
            if (page > 0) {
                --page;
            }
            break;
        case "next":
            if ((page + 1) * size < count) {
                ++page;
            }
            break;
        case "last":
            page = count / size;
            break;
        default:
            if (id.equals(this.order)) {
                desc = !desc;
            } else {
                order = id;
                desc = false;
            }
            page = 0;
            break;
        }
        loadEntities();
    }
}
